/*
 * @Author: wuqingchun
 * @Date: 2024-01-10 19:12:50
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-01-10 19:44:37
 */
#ifndef __FTPC_LOG_H__
#define __FTPC_LOG_H__

typedef enum tagFtpcLogLevel{
	FTPC_LOG_LEVEL_DEBUG = 0 ,
	FTPC_LOG_LEVEL_INFO = 1,
	FTPC_LOG_LEVEL_NOTICE = 2,
	FTPC_LOG_LEVEL_WARN = 3,
	FTPC_LOG_LEVEL_ERROR = 4,
	FTPC_LOG_LEVEL_FATAL = 5
}FTPC_LOG_LEVEL_E;

#define FTPC_LOG_LEVEL_FLAG_DEBUG  (0x1 << FTPC_LOG_LEVEL_DEBUG)
#define FTPC_LOG_LEVEL_FLAG_INFO   (0x1 << FTPC_LOG_LEVEL_INFO)
#define FTPC_LOG_LEVEL_FLAG_NOTICE (0x1 << FTPC_LOG_LEVEL_NOTICE)
#define FTPC_LOG_LEVEL_FLAG_WARN   (0x1 << FTPC_LOG_LEVEL_WARN)
#define FTPC_LOG_LEVEL_FLAG_ERROR  (0x1 << FTPC_LOG_LEVEL_ERROR)
#define FTPC_LOG_LEVEL_FLAG_FATAL  (0x1 << FTPC_LOG_LEVEL_FATAL)


typedef struct tagFtpcLog
{
    int iLevel;                                    // 日志级别
    int iFd;                                       // 日志文件描述符
}FTPC_LOG_S;

extern FTPC_LOG_S g_stFTPCLog;
/*
 在C语言编程过程中，我们常常会实现一些可变参数的函数调用(类scanf、printf函数)，变参函数在我们编程过程中带来了很大的方便，
 但是也有一些问题，即我们在调用可变参数的函数的时候，默认情况下编译器检查不出可变参数的类型或者个数是否正确，
 这就导致使用变参函数的时候，若稍不注意写错了参数，很难被发现和定位。
​ _attribute_((format(printf, 2, 3)))的作用就是用来解决这个问题，它用于函数声明，作用是提示编译器检查函数调用的过程中，
可变参数部分按照printf的规则进行检查，若参数的个数或者类型不匹配，编译过程中将会发出警告,这就使得上面提到的问题在编译期间就能发现。
注意编译时要加上 –Wall才可以。

// 用法原型
// archetype:为按照那种风格进行校验，如printf/scanf等
// string-index:格式化format字符串所在的位置,如void test(testA, format,...)，此时为2
// first-to-check:第一个可变参数的位置，如void test(testA, format,...)，此时为3
__attribute__((format(archetype, string-index, first-to-check)))

注意：string-index和first-to-check值选取的时候，若变参函数是类成员函数，这时函数展开后第一个参数为this指针，这个也要考虑到位置中。
*/

// 输出到日志文件
void FTPC_log_Output
(
    FTPC_LOG_LEVEL_E enLevel, 
    const char* pcSrcFile, 
    unsigned int uiLineNo, 
    const char* pcFuncName, 
    const char* pcFmt, 
    ...
) __attribute__((format(printf, 5, 6)));


#define FTPC_LOG_DEBUG(fmt, ...) \
    if (BIT_TEST(g_stFTPCLog.iLevel, FTPC_LOG_LEVEL_FLAG_DEBUG)) \
        FTPC_log_Output(FTPC_LOG_LEVEL_DEBUG, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define FTPC_LOG_INFO(fmt, ...) \
    if (BIT_TEST(g_stFTPCLog.iLevel, FTPC_LOG_LEVEL_FLAG_INFO)) \
        FTPC_log_Output(FTPC_LOG_LEVEL_INFO, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define FTPC_LOG_NOTICE(fmt, ...) \
    if (BIT_TEST(g_stFTPCLog.iLevel, FTPC_LOG_LEVEL_FLAG_NOTICE)) \
        FTPC_log_Output(FTPC_LOG_LEVEL_NOTICE, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define FTPC_LOG_WARN(fmt, ...) \
    if (BIT_TEST(g_stFTPCLog.iLevel, FTPC_LOG_LEVEL_FLAG_WARN)) \
        FTPC_log_Output(FTPC_LOG_LEVEL_WARN, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define FTPC_LOG_ERROR(fmt, ...) \
    if (BIT_TEST(g_stFTPCLog.iLevel, FTPC_LOG_LEVEL_FLAG_ERROR)) \
        FTPC_log_Output(FTPC_LOG_LEVEL_ERROR, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)
#define FTPC_LOG_FATAL(fmt, ...) \
    if (BIT_TEST(g_stFTPCLog.iLevel, FTPC_LOG_LEVEL_FLAG_FATAL)) \
        FTPC_log_Output(FTPC_LOG_LEVEL_FATAL, __FILE__, __LINE__, __FUNCTION__, fmt, ##__VA_ARGS__)

int FTPC_log_Init(void);
void FTPC_log_Exit(void);

#endif //__FTPC_LOG_H__